﻿using PageAdmin.Utils.ORM;
using PageAdmin.Utils.ORM.PropertyDescriptor;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;

namespace PageAdmin.Utils.BLL
{
    /// <summary>
    /// 列表获取接口，支持单页和分页查询
    /// </summary>
    public class GetListDataBLL<T> : IGetListData<T> where T : class, new()
    {
        /// <summary>
        /// 执行前的委托
        /// </summary>
        public Func<TipsInfo> Executing { get; set; }

        /// <summary>
        /// 执行后的委托
        /// </summary>
        public Action<IEnumerable<T>> Executed { get; set; }


        /// <summary>
        /// 自定义附件查询条件
        /// </summary>
        public string QueryCondition { get; set; } = "";

        /// <summary>
        /// 自定义附加参数，和_QueryCondition
        /// </summary>
        public DynamicDbParameters QueryDbParameters { get; set; } = new DynamicDbParameters();

        /// <summary>
        /// 默认排序方式
        /// </summary>
        public string OrderBy { get; set; } = "id desc";

        /// <summary>
        /// 显示的数量
        /// </summary>
        public int? ShowNumber { get; set; } = null;

        /// <summary>
        /// 数据库对象
        /// </summary>
        protected DataBaseContext _DbContext;

        public GetListDataBLL(DataBaseContext dataBaseContext)
        {
            _DbContext = dataBaseContext;
        }


        private TipsInfo init(NameValueCollection form, PageInfo PageInfo = null)
        {
            TipsInfo tipsInfo = new TipsInfo();
            if (Executing != null)
            {
                tipsInfo = Executing.Invoke(); //执行前
            }
            if (tipsInfo.State == 0)
            {
                return tipsInfo;
            }
            initListConfig(form, PageInfo);
            return tipsInfo;
        }

        /// <summary>
        /// 查询接口实现类，单页查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IEnumerable<T> GetList(NameValueCollection form)
        {
            TipsInfo tipsInfo = init(form);
            if (tipsInfo.State == 0)
            {
                return new List<T>();
            }
            DbSet<T> dbset = _DbContext.DbSet<T>();
            IEnumerable<T> list = dbset.Query(QueryCondition, OrderBy, ShowNumber, QueryDbParameters);
            var proInfo = dbset.ModelType.GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
            if (proInfo != null)
            {
                foreach (T item in list)
                {
                    int id = (int)proInfo.GetValue(item, null);
                    SetRelativeData(dbset, item, id);
                }
            }
            Executed?.Invoke(list);
            return list;
        }

        /// <summary>
        /// 查询接口实现类，分页查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IEnumerable<T> GetPageList(NameValueCollection form, PageInfo PageInfo)
        {
            TipsInfo tipsInfo = init(form, PageInfo);
            if (tipsInfo.State == 0)
            {
                return new List<T>();
            }
            DbSet<T> dbset = _DbContext.DbSet<T>();
            IEnumerable<T> list = dbset.PagingQuery(PageInfo, QueryCondition, OrderBy, QueryDbParameters);
            var proInfo = dbset.ModelType.GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
            if (proInfo != null)
            {
                foreach (T item in list)
                {
                    int id = (int)proInfo.GetValue(item, null);
                    SetRelativeData(dbset, item, id);
                }
            }
            Executed?.Invoke(list);
            return list;
        }


        /// <summary>
        /// 设置相关数据,附件数据，中间表数据，关联表数据
        /// </summary>
        protected void SetRelativeData(DbSet<T> dbSet, T model, int detailId)
        {
            AttachmentDataBLL.SetAttachmentListProperty(dbSet, model, detailId,false); //设置附件
            SubdataBLL.SetSubdata(_DbContext, model, detailId, false); //设置子数据
            RelateDataBLL.SetRelateData(_DbContext, model,false);//设置管理数据
        }

        /// <summary>
        /// 初始化查询参数
        /// </summary>
        /// <param name="form"></param>
        protected void initListConfig(NameValueCollection form, PageInfo pageInfo)
        {
            if(form==null)
            {
                return;
            }
            if (pageInfo != null)
            {
                int pageSize = StringHelper.Format<int>(form["PageSize"]);
                int currentPage = StringHelper.Format<int>(form["currentPage"]);
                if (pageSize > 0)
                {
                    pageInfo.PageSize = pageSize;
                }
                if (currentPage > 0)
                {
                    pageInfo.CurrentPage = currentPage;
                }
            }
            string orderBy = form["OrderBy"];
            if (!string.IsNullOrEmpty(orderBy))
            {
                OrderBy = orderBy;
            }
            CreateCondition(form);
            if (QueryCondition.StartsWith(" and"))
            {
                QueryCondition = QueryCondition.Substring(4);
            }
        }

        /// <summary>
        /// 构造查询条件
        /// </summary>
        /// <param name="searchType"></param>
        /// <param name="valueType"></param>
        /// <param name="searchField"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private void CreateCondition(NameValueCollection form)
        {
            IEnumerable<ModelFieldMapped> modelFieldMappedList = _DbContext.DbSet<T>().ModelFieldMappingList.Where(c => c.IsSearchMapped == true);
            foreach (ModelFieldMapped modelFieldMapped in modelFieldMappedList)
            {
                string paramValue = form[modelFieldMapped.PropertyName];
                if (modelFieldMapped.SearchMappedDescriptor.SearchIgnoreEmpty && string.IsNullOrEmpty(paramValue))
                {
                    continue;
                }
                switch (modelFieldMapped.SearchMappedDescriptor.SearchTypeEnum)
                {
                    case SearchTypeEnum.Fuzzyquery:
                        QueryCondition += FuzzySql(modelFieldMapped.ValueType, modelFieldMapped.FieldName, paramValue);
                        break;

                    case SearchTypeEnum.PreciseQuery:
                        QueryCondition += PreciseSql(modelFieldMapped.ValueType, modelFieldMapped.FieldName, paramValue);
                        break;

                    case SearchTypeEnum.InQuery:
                        QueryCondition += InSql(modelFieldMapped.ValueType, modelFieldMapped.FieldName, paramValue);
                        break;

                    case SearchTypeEnum.RangeQuery:
                        QueryCondition += RangeSql(modelFieldMapped.ValueType, modelFieldMapped.FieldName, paramValue);
                        break;
                }
            }
        }

        /// <summary>
        /// 模糊匹配
        /// </summary>
        /// <param name="valueType"></param>
        /// <param name="searchField"></param>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        private string FuzzySql(string valueType, string searchField, string paramValue)
        {
            //等号开头则改为精确匹配
            if (paramValue.StartsWith("=") && paramValue.Length > 1)
            {
                paramValue = paramValue.Substring(1, paramValue.Length - 1);
                return PreciseSql(valueType, searchField, paramValue);
            }
            string rv = "";
            switch (valueType)
            {
                case "String":
                    rv = " and [" + searchField + "] like @" + searchField;
                    QueryDbParameters.Add(searchField, "%" + paramValue + "%");
                    break;
                default:
                    break;
            }
            return rv;
        }

        /// <summary>
        /// 精确匹配
        /// </summary>
        /// <param name="valueType"></param>
        /// <param name="searchField"></param>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        private string PreciseSql(string valueType, string searchField, string paramValue)
        {

            string rv = "";
            switch (valueType)
            {
                case "String":
                    rv = " and [" + searchField + "]=@" + searchField;
                    QueryDbParameters.Add(searchField, paramValue);
                    break;

                case "DateTime":

                    rv = " and [" + searchField + "]=@" + searchField;
                    QueryDbParameters.Add(searchField, StringHelper.Format<DateTime>(paramValue));

                    break;

                case "Int16":

                    rv = " and [" + searchField + "]=@" + searchField;
                    QueryDbParameters.Add(searchField, StringHelper.Format<Int16>(paramValue));

                    break;

                case "Byte":

                    rv = " and [" + searchField + "]=@" + searchField;
                    QueryDbParameters.Add(searchField, StringHelper.Format<byte>(paramValue));
                    var state = StringHelper.Format<byte>(paramValue);
                    break;

                case "Int64":

                    rv = " and [" + searchField + "]=@" + searchField;
                    QueryDbParameters.Add(searchField, StringHelper.Format<long>(paramValue));

                    break;

                case "Int32":

                    rv = " and [" + searchField + "]=@" + searchField;
                    QueryDbParameters.Add(searchField, StringHelper.Format<int>(paramValue));

                    break;

                case "Double":

                    rv = " and [" + searchField + "]=@" + searchField;
                    QueryDbParameters.Add(searchField, StringHelper.Format<double>(paramValue));

                    break;

                case "Decimal":

                    rv = " and [" + searchField + "]=@" + searchField;
                    QueryDbParameters.Add(searchField, StringHelper.Format<decimal>(paramValue));

                    break;

                case "Float":

                    rv = " and [" + searchField + "]=@" + searchField;
                    QueryDbParameters.Add(searchField, StringHelper.Format<float>(paramValue));

                    break;
            }
            return rv;
        }

        /// <summary>
        /// 精确匹配In
        /// </summary>
        /// <param name="valueType"></param>
        /// <param name="searchField"></param>
        /// <param name="paramValue"></param>
        /// <returns></returns>

        private string InSql(string valueType, string searchField, string paramValue)
        {
            string rv = "";
            paramValue = StringHelper.Unique<string>(paramValue);
            string[] AparamValue = paramValue.Split(',');
            if (AparamValue.Length == 1)
            {
                rv = PreciseSql(valueType, searchField, paramValue);
            }
            else
            {
                switch (valueType)
                {
                    case "String":
                        paramValue = StringHelper.SqlFormat<string>(AparamValue);
                        rv = " and [" + searchField + "] in (" + paramValue + ")";
                        break;

                    case "DateTime":
                        paramValue = StringHelper.SqlFormat<DateTime>(AparamValue);
                        rv = " and [" + searchField + "] in (" + paramValue + ")";
                        break;

                    default:
                        paramValue = StringHelper.SqlFormat<int>(AparamValue);
                        rv = " and [" + searchField + "] in (" + paramValue + ")";
                        break;
                }

            }
            return rv;
        }

        /// <summary>
        /// 范围匹配
        /// </summary>
        /// <param name="valueType"></param>
        /// <param name="searchField"></param>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        private string RangeSql(string valueType, string searchField, string paramValue)
        {
            string rv = "";
            string[] AparamValue = paramValue.Split(',');
            //两个值用半角逗号隔开
            if (AparamValue.Length == 2)
            {

                switch (valueType)
                {
                    default:
                        rv = "";
                        break;

                    case "Datetime":
                        if (StringHelper.IsDateTime(AparamValue[0]) && StringHelper.IsDateTime(AparamValue[1]))
                        {
                            rv = " and [" + searchField + "] between '{0}' and '{1}'";
                            rv = string.Format(rv, AparamValue[0], AparamValue[1]);
                        }
                        break;

                    case "Double":
                        if (StringHelper.IsDouble(AparamValue[0]) && StringHelper.IsDouble(AparamValue[1]))
                        {
                            rv = " and [" + searchField + "] between " + AparamValue[0] + "_1 and " + AparamValue[1];
                        }
                        break;
                    case "Float":
                        if (StringHelper.IsFloat(AparamValue[0]) && StringHelper.IsFloat(AparamValue[1]))
                        {
                            rv = " and [" + searchField + "] between " + AparamValue[0] + "_1 and " + AparamValue[1];
                        }
                        break;


                    case "Int64":
                        if (StringHelper.IsLong(AparamValue[0]) && StringHelper.IsLong(AparamValue[1]))
                        {
                            rv = " and [" + searchField + "] between " + AparamValue[0] + "_1 and " + AparamValue[1];
                        }
                        break;

                    case "Int32":
                        if (StringHelper.IsInt(AparamValue[0]) && StringHelper.IsInt(AparamValue[1]))
                        {
                            rv = " and [" + searchField + "] between " + AparamValue[0] + "_1 and " + AparamValue[1];
                        }
                        break;

                    case "Int16":
                        if (StringHelper.IsShort(AparamValue[0]) && StringHelper.IsShort(AparamValue[1]))
                        {
                            rv = " and [" + searchField + "] between " + AparamValue[0] + "_1 and " + AparamValue[1];
                        }
                        break;

                    case "Byte":
                        if (StringHelper.IsByte(AparamValue[0]) && StringHelper.IsByte(AparamValue[1]))
                        {
                            rv = " and [" + searchField + "] between " + AparamValue[0] + "_1 and " + AparamValue[1];
                        }
                        break;
                }

            }
            return rv;
        }


    }
}